﻿//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Expenses;
using Expenses.ExpenseService;
using Windows.UI;
using Windows.UI.Xaml;

namespace Expenses.ViewModels
{
    public class ExpenseReportsViewModel : ViewModelBase
    {
        #region Properties

        private int numberSaved = 0;
        public int NumberSaved
        {
            get
            { return numberSaved; }

            set
            {
                if (numberSaved == value)
                { return; }

                numberSaved = value;
                RaisePropertyChanged("NumberSaved");
            }
        }

        private decimal amountSaved = 0M;
        public decimal AmountSaved
        {
            get
            { return amountSaved; }

            set
            {
                if (amountSaved == value)
                { return; }

                amountSaved = value;
                RaisePropertyChanged("AmountSaved");
            }
        }

        private int numberPending = 0;
        public int NumberPending
        {
            get
            { return numberPending; }

            set
            {
                if (numberPending == value)
                { return; }

                numberPending = value;
                RaisePropertyChanged("NumberPending");
            }
        }

        private decimal amountPending = 0M;
        public decimal AmountPending
        {
            get
            { return amountPending; }

            set
            {
                if (amountPending == value)
                { return; }

                amountPending = value;
                RaisePropertyChanged("AmountPending");
            }
        }

        private int numberOwedToEmployee = 0;
        public int NumberOwedToEmployee
        {
            get
            { return numberOwedToEmployee; }

            set
            {
                if (numberOwedToEmployee == value)
                { return; }

                numberOwedToEmployee = value;
                RaisePropertyChanged("NumberOwedToEmployee");
            }
        }

        private decimal amountOwedToEmployee = 0M;
        public decimal AmountOwedToEmployee
        {
            get
            { return amountOwedToEmployee; }

            set
            {
                if (amountOwedToEmployee == value)
                { return; }

                amountOwedToEmployee = value;
                RaisePropertyChanged("AmountOwedToEmployee");
            }
        }

        private decimal amountOwedToCreditCard = 0M;
        public decimal AmountOwedToCreditCard
        {
            get
            { return amountOwedToCreditCard; }

            set
            {
                if (amountOwedToCreditCard == value)
                { return; }

                amountOwedToCreditCard = value;
                RaisePropertyChanged("AmountOwedToCreditCard");
            }
        }

        private int numberWaitingApproval = 0;
        public int NumberWaitingApproval
        {
            get
            { return numberWaitingApproval; }

            set
            {
                if (numberWaitingApproval == value)
                { return; }

                numberWaitingApproval = value;
                RaisePropertyChanged("NumberWaitingApproval");
            }
        }

        private decimal amountWaitingApproval = 0M;
        public decimal AmountWaitingApproval
        {
            get
            { return amountWaitingApproval; }

            set
            {
                if (amountWaitingApproval == value)
                { return; }

                amountWaitingApproval = value;
                RaisePropertyChanged("AmountWaitingApproval");
            }
        }

        public ObservableCollection<SortType> SortTypes { get; set; }

        public ObservableCollection<GroupInfoList<object>> GroupedExpenseReports
        {
            get
            {
                return groupedExpenseReports;
            }

            set
            {
                groupedExpenseReports = value;
                RaisePropertyChanged("GroupedExpenseReports");
            }
        }

        public ObservableCollection<ExpenseReportViewModel> ExpenseReports
        {
            get
            {
                return expenseReports;
            }

            set
            {
                expenseReports = value;
                RaisePropertyChanged("ExpenseReports");
            }
        }

        public ObservableCollection<ExpenseReportViewModel> PendingExpenseReports
        {
            get
            {
                return pendingExpenseReports;
            }

            set
            {
                pendingExpenseReports = value;
                RaisePropertyChanged("PendingExpenseReports");
            }
        }

        public ObservableCollection<ExpenseReportViewModel> SavedExpenseReports
        {
            get
            {
                return savedExpenseReports;
            }

            set
            {
                savedExpenseReports = value;
                RaisePropertyChanged("SavedExpenseReports");
            }
        }

        public ObservableCollection<ExpenseReportViewModel> WaitingForApprovalExpenseReports
        {
            get
            {
                return waitingForApprovalExpenseReports;
            }

            set
            {
                waitingForApprovalExpenseReports = value;
                RaisePropertyChanged("WaitingForApprovalExpenseReports");
            }
        }

        public ObservableCollection<ExpenseReportViewModel> ApprovedExpenseReports
        {
            get
            {
                return approvedExpenseReports;
            }

            set
            {
                approvedExpenseReports = value;
                RaisePropertyChanged("ApprovedExpenseReports");
            }
        }

        public SortType SortType
        {
            get
            {
                return sortType;
            }

            set
            {
                if (sortType == value)
                {
                    return;
                }

                sortType = value;
                RaisePropertyChanged("SortType");

                //update the grouping because the sort/filter type changed
                GroupExpenseReports();
            }
        }

        #endregion

        private SortType sortType = SortType.ItemType;
        private ObservableCollection<GroupInfoList<object>> groupedExpenseReports;
        private ObservableCollection<ExpenseReportViewModel> expenseReports;
        private ObservableCollection<ExpenseReportViewModel> pendingExpenseReports;
        private ObservableCollection<ExpenseReportViewModel> savedExpenseReports;
        private ObservableCollection<ExpenseReportViewModel> waitingForApprovalExpenseReports;
        private ObservableCollection<ExpenseReportViewModel> approvedExpenseReports;

        public ExpenseReportsViewModel()
        {
            this.groupedExpenseReports = new ObservableCollection<GroupInfoList<object>>();
            this.SortTypes = new ObservableCollection<SortType>();
            this.SortTypes.Add(SortType.Age);
            this.SortTypes.Add(SortType.Amount);
        }

        public async Task<ObservableCollection<ExpenseReportViewModel>>
            GetExpenseReports()
        {
            var expensesService = new ExpenseServiceClient();
            List<ExpenseReport> expenseReports = await
                expensesService.GetExpenseReportsAsync(
                App.EmployeeId);

            string employeeName = await
                GetEmployeeName(App.EmployeeId);

            var reports = from report in expenseReports
                          select new ExpenseReportViewModel()
                          {
                              Amount = report.Amount,
                              Approver = report.Approver,
                              CostCenter = report.CostCenter,
                              DateResolved = report.DateResolved,
                              DateSubmitted = report.DateSubmitted,
                              DateSaved = report.DateSaved,
                              EmployeeId = report.EmployeeId,
                              Notes = report.Notes,
                              OwedToCreditCard = report.OwedToCreditCard,
                              OwedToEmployee = report.OwedToEmployee,
                              Purpose = report.Purpose,
                              ReportNumber = report.ReportNumber,
                              Status = (StatusType)report.Status,
                              EmployeeName = employeeName
                          };
            this.ExpenseReports = new
                ObservableCollection<ExpenseReportViewModel>(reports);

            return this.ExpenseReports;
        }

        public async Task<ObservableCollection<ExpenseReportViewModel>>
            GetSavedExpenseReports()
        {
            var expensesService = new ExpenseServiceClient();
            List<ExpenseReport> expenseReports = await
                expensesService.GetSavedExpenseReportsAsync(
                App.EmployeeId);

            string employeeName = await
                GetEmployeeName(App.EmployeeId);

            var savedReports = from report in expenseReports
                               where (StatusType)report.Status == StatusType.Saved
                               select new ExpenseReportViewModel()
                               {
                                   Amount = report.Amount,
                                   Approver = report.Approver,
                                   CostCenter = report.CostCenter,
                                   DateResolved = report.DateResolved,
                                   DateSubmitted = report.DateSubmitted,
                                   DateSaved = report.DateSaved,
                                   EmployeeId = report.EmployeeId,
                                   Notes = report.Notes,
                                   OwedToCreditCard = report.OwedToCreditCard,
                                   OwedToEmployee = report.OwedToEmployee,
                                   Purpose = report.Purpose,
                                   ReportNumber = report.ReportNumber,
                                   Status = (StatusType)report.Status,
                                   EmployeeName = employeeName
                               };
            this.SavedExpenseReports = new ObservableCollection<ExpenseReportViewModel>(savedReports);

            numberSaved = this.SavedExpenseReports.Count();
            amountSaved = Convert.ToDecimal(
                (from report in this.SavedExpenseReports
                 select report.Amount).Sum());

            foreach (ExpenseReportViewModel report in this.SavedExpenseReports)
            {
                report.ContainsLateCharges = await CountLateCharges(report.ReportNumber);
                if (report.ContainsLateCharges)
                {
                    report.ForegroundColor = "#FFD33131";
                }
                else
                {
                    report.ForegroundColor = "Black";
                }
            }

            return this.SavedExpenseReports;
        }

        public async Task<ObservableCollection<ExpenseReportViewModel>>
            GetPendingExpenseReports()
        {
            var expensesService = new ExpenseServiceClient();
            List<ExpenseReport> expenseReports = await
                expensesService.GetPendingExpenseReportsAsync(
                App.EmployeeId);

            string employeeName = await
                GetEmployeeName(App.EmployeeId);

            var pendingReports = from report in expenseReports
                                 where (StatusType)report.Status == StatusType.Pending
                                 select new ExpenseReportViewModel()
                                 {
                                     Amount = report.Amount,
                                     Approver = report.Approver,
                                     CostCenter = report.CostCenter,
                                     DateResolved = report.DateResolved,
                                     DateSubmitted = report.DateSubmitted,
                                     DateSaved = report.DateSaved,
                                     EmployeeId = report.EmployeeId,
                                     Notes = report.Notes,
                                     OwedToCreditCard = report.OwedToCreditCard,
                                     OwedToEmployee = report.OwedToEmployee,
                                     Purpose = report.Purpose,
                                     ReportNumber = report.ReportNumber,
                                     Status = (StatusType)report.Status,
                                     EmployeeName = employeeName
                                 };

            foreach (ExpenseReportViewModel report in pendingReports)
            {
                report.EmployeeName = await GetEmployeeName(report.EmployeeId);
            }

            this.PendingExpenseReports = new ObservableCollection<ExpenseReportViewModel>(pendingReports);

            foreach (ExpenseReportViewModel report in this.PendingExpenseReports)
            {
                report.ContainsLateCharges = await CountLateCharges(report.ReportNumber);

                if (DateTime.Today.Subtract((DateTime)report.DateSubmitted).Days <= 3)
                {
                    report.BackgroundColor = "#FF2CA206";
                    if (report.OwedToEmployee > 0)
                    {
                        report.ForegroundColor = "#FF2CA206";
                    }
                    else
                    {
                        report.ForegroundColor = "#FF000000";
                    }
                }
                else if (DateTime.Today.Subtract((DateTime)report.DateSubmitted).Days > 3
                         && DateTime.Today.Subtract((DateTime)report.DateSubmitted).Days <= 7)
                {
                    report.BackgroundColor = "#FFffbe0a";
                    if (report.OwedToEmployee > 0)
                    {
                        report.ForegroundColor = "#FFffbe0a";
                    }
                    else
                    {
                        report.ForegroundColor = "#FF000000";
                    }
                }
                else
                {
                    report.BackgroundColor = "#FFD33131";
                    if (report.OwedToEmployee > 0)
                    {
                        report.ForegroundColor = "#FFD33131";
                    }
                    else
                    {
                        report.ForegroundColor = "#FF000000";
                    }
                }
            }

            numberPending = this.PendingExpenseReports.Count();
            amountPending = Convert.ToDecimal(
                (from report in this.PendingExpenseReports
                 select report.Amount).Sum());

            amountOwedToEmployee = Convert.ToDecimal(
                (from report in this.PendingExpenseReports
                 select report.OwedToEmployee).Sum());
            amountOwedToCreditCard = Convert.ToDecimal(
                (from report in this.PendingExpenseReports
                 select report.OwedToCreditCard).Sum());

            return this.PendingExpenseReports;
        }

        public async Task<ObservableCollection<ExpenseReportViewModel>>
            GetWaitingForApprovalExpenseReports()
        {
            var expensesService = new ExpenseServiceClient();
            List<ExpenseReport> expenseReports = await
                expensesService.GetWaitingForApprovalExpenseReportsAsync(
                App.Employee.EmployeeId);

            string employeeName = await
                GetEmployeeName(App.EmployeeId);

            var waitingReports = from report in expenseReports
                                 where (StatusType)report.Status == StatusType.Pending
                                 select new ExpenseReportViewModel()
                                 {
                                     Amount = report.Amount,
                                     Approver = report.Approver,
                                     CostCenter = report.CostCenter,
                                     DateResolved = report.DateResolved,
                                     DateSubmitted = report.DateSubmitted,
                                     DateSaved = report.DateSaved,
                                     EmployeeId = report.EmployeeId,
                                     Notes = report.Notes,
                                     OwedToCreditCard = report.OwedToCreditCard,
                                     OwedToEmployee = report.OwedToEmployee,
                                     Purpose = report.Purpose,
                                     ReportNumber = report.ReportNumber,
                                     Status = (StatusType)report.Status,
                                     EmployeeName = employeeName
                                 };

            this.WaitingForApprovalExpenseReports = new
                ObservableCollection<ExpenseReportViewModel>(waitingReports);

            foreach (ExpenseReportViewModel report in this.WaitingForApprovalExpenseReports)
            {
                report.EmployeeName = await GetEmployeeName(report.EmployeeId);
                report.ContainsLateCharges = await CountLateCharges(report.ReportNumber);

                if (DateTime.Today.Subtract((DateTime)report.DateSubmitted).Days <= 3)
                {
                    report.BackgroundColor = "#FF2CA206";
                    if (report.OwedToEmployee > 0)
                    {
                        report.ForegroundColor = "#FF2CA206";
                    }
                    else
                    {
                        report.ForegroundColor = "#FF000000";
                    }
                }
                else if (DateTime.Today.Subtract((DateTime)report.DateSubmitted).Days > 3
                         && DateTime.Today.Subtract((DateTime)report.DateSubmitted).Days <= 7)
                {
                    report.BackgroundColor = "#FFffbe0a";
                    if (report.OwedToEmployee > 0)
                    {
                        report.ForegroundColor = "#FFffbe0a";
                    }
                    else
                    {
                        report.ForegroundColor = "#FF000000";
                    }
                }
                else
                {
                    report.BackgroundColor = "#FFD33131";
                    if (report.OwedToEmployee > 0)
                    {
                        report.ForegroundColor = "#FFD33131";
                    }
                    else
                    {
                        report.ForegroundColor = "#FF000000";
                    }
                }
            }

            numberWaitingApproval = this.WaitingForApprovalExpenseReports.Count();
            amountWaitingApproval = Convert.ToDecimal(
                (from report in this.WaitingForApprovalExpenseReports
                 select report.Amount).Sum());

            return this.WaitingForApprovalExpenseReports;
        }

        public async Task<ObservableCollection<ExpenseReportViewModel>>
            GetApprovedExpenseReports()
        {
            var expensesService = new ExpenseServiceClient();
            List<ExpenseReport> expenseReports = await
                expensesService.GetApprovedExpenseReportsAsync(
                App.EmployeeId);

            string employeeName = await
                GetEmployeeName(App.EmployeeId);

            var approvedReports = from report in expenseReports
                                  where (StatusType)report.Status == StatusType.Approved
                                  select new ExpenseReportViewModel()
                                  {
                                      Amount = report.Amount,
                                      Approver = report.Approver,
                                      CostCenter = report.CostCenter,
                                      DateResolved = report.DateResolved,
                                      DateSubmitted = report.DateSubmitted,
                                      DateSaved = report.DateSaved,
                                      EmployeeId = report.EmployeeId,
                                      Notes = report.Notes,
                                      OwedToCreditCard = report.OwedToCreditCard,
                                      OwedToEmployee = report.OwedToEmployee,
                                      Purpose = report.Purpose,
                                      ReportNumber = report.ReportNumber,
                                      Status = (StatusType)report.Status,
                                      EmployeeName = employeeName
                                  };

            foreach (ExpenseReportViewModel report in approvedReports)
            {
                report.EmployeeName = await GetEmployeeName(report.EmployeeId);
            }

            this.ApprovedExpenseReports = new
                ObservableCollection<ExpenseReportViewModel>(approvedReports);

            GroupApprovedExpenseReportsByAge();

            return this.ApprovedExpenseReports;
        }

        public async Task<decimal> GetAmountOwedToCreditCard(int employeeId)
        {
            var expensesService = new ExpenseServiceClient();
            decimal amount = await
                expensesService.GetAmountOwedToCreditCardAsync(employeeId);
            return amount;
        }

        public async Task<decimal> GetAmountOwedToEmployee(int employeeId)
        {
            var expensesService = new ExpenseServiceClient();
            decimal amount = await
                expensesService.GetAmountOwedToEmployeeAsync(employeeId);
            return amount;
        }

        public async Task<bool> CountLateCharges(int reportNumber)
        {
            var expensesService = new ExpenseServiceClient();
            var expenseItems = await
                expensesService.GetExpenseItemsAsync(reportNumber);

            int lateCharges = (from item in expenseItems
                               where DateTime.Today.Subtract(item.ExpenseDate).Days >= 45
                               select item.BilledAmount).Count();

            return (lateCharges > 0);
        }

        public async Task<string> GetEmployeeName(int employeeId)
        {
            var expensesService = new ExpenseServiceClient();
            string employeeName = await expensesService.GetEmployeeNameAsync(employeeId);
            return employeeName;
        }

        public void GroupExpenseReports()
        {
            switch (this.SortType)
            {
                case SortType.Category:
                    GroupExpenseReportsByCategory();
                    break;
                case SortType.Age:
                    GroupExpenseReportsByAge();
                    break;
                case SortType.Amount:
                    GroupExpenseReportsByAmount();
                    break;
                case SortType.Submitter:
                    GroupExpenseReportsBySubmitter();
                    break;
                case SortType.ItemType:
                    GroupExpenseReportsByAge();
                    break;
                default:
                    break;
            }
        }

        internal void GroupApprovedExpenseReportsByAge()
        {
            var query = from expenseReport in this.ApprovedExpenseReports
                        orderby expenseReport.DateResolved descending
                        group expenseReport by ((DateTime)expenseReport.DateResolved).Year into g
                        select new
                        {
                            GroupName = g.Key,
                            Count = g.Count(),
                            Amount = g.Sum(e => e.Amount),
                            Items = g
                        };

            this.GroupedExpenseReports.Clear();
            var groups = new GroupInfoList<object>();
            foreach (var group in query)
            {
                var items = new GroupInfoList<object>();
                items.Key = group.GroupName;
                items.Amount = System.Convert.ToDecimal(group.Amount);
                items.Summary = string.Format("{0} for {1:C}",
                    group.Count, items.Amount);
                items.ImportList(group.Items);
                this.GroupedExpenseReports.Add(items);
            }
        }

        internal GroupInfoList<object> GroupExpenseReportsByCategory()
        {
            var query = from expenseReport in ExpenseReports
                        orderby expenseReport.Status
                        group expenseReport by expenseReport.Status into g
                        select new
                        {
                            GroupName = g.Key,
                            Count = g.Count(),
                            Amount = g.Sum(e => e.Amount),
                            Items = g
                        };

            var groups = new GroupInfoList<object>();
            foreach (var group in query)
            {
                var groupedExpenses = new GroupInfoList<object>();
                string statusTypeName = null;
                StatusType statusType = (StatusType)group.GroupName;
                switch (statusType)
                {
                    case StatusType.Approved:
                        statusTypeName = "Approved";
                        break;
                    case StatusType.Pending:
                        statusTypeName = "Pending";
                        break;
                    case StatusType.Saved:
                        statusTypeName = "Saved";
                        break;
                    default:
                        break;
                }
                groupedExpenses.Key = string.Format(
                    "{0} ({1} items for {2:C})", statusTypeName, group.Count, group.Amount);
                groupedExpenses.Amount = System.Convert.ToDecimal(group.Amount);
                foreach (var item in groupedExpenses)
                {
                    groupedExpenses.Add(item);
                }
                groups.Add(groupedExpenses);
            }

            return groups;
        }

        internal void GroupExpenseReportsByAge()
        {
            var query = from expenseReport in ExpenseReports
                        orderby expenseReport.DateResolved descending
                        group expenseReport by ((DateTime)expenseReport.DateResolved).Year into g
                        select new
                        {
                            GroupName = g.Key,
                            Count = g.Count(),
                            Amount = g.Sum(e => e.Amount),
                            Items = g
                        };

            this.GroupedExpenseReports.Clear();
            var groups = new GroupInfoList<object>();
            foreach (var group in query)
            {
                var items = new GroupInfoList<object>();
                items.Key = group.GroupName;
                items.Amount = System.Convert.ToDecimal(group.Amount);
                items.Summary = string.Format("{0} for {1:C}",
                    group.Count, items.Amount);
                items.ImportList(group.Items);
                this.GroupedExpenseReports.Add(items);
            }
        }

        internal void GroupExpenseReportsByAmount()
        {
        }

        internal GroupInfoList<object> GroupExpenseReportsBySubmitter()
        {
            var query = from expenseReport in ExpenseReports
                        orderby expenseReport.Status
                        group expenseReport by expenseReport.EmployeeId into g
                        select new
                        {
                            GroupName = g.Key,
                            Count = g.Count(),
                            Amount = g.Sum(e => e.Amount),
                            Items = g
                        };

            var groups = new GroupInfoList<object>();
            foreach (var group in query)
            {
                var groupedExpenses = new GroupInfoList<object>();
                groupedExpenses.Key = string.Format(
                    "{0} ({1} items for {2:C})", group.GroupName, group.Count, group.Amount);
                groupedExpenses.Amount = System.Convert.ToDecimal(group.Amount);
                foreach (var item in groupedExpenses)
                {
                    groupedExpenses.Add(item);
                }
                groups.Add(groupedExpenses);
            }

            return groups;
        }

    }

}
